﻿#ifndef WTHTTP_H_
#define WTHTTP_H_

// C++ lib import
#include <functional>

// Qt lib import
#include <QObject>
#include <QVariant>
#include <QJSValue>
#include <QElapsedTimer>
#include <QTimer>
#include <QPointer>
#include <QNetworkRequest>
#include <QQmlEngine>

class QNetworkAccessManager;
class QNetworkReply;
class QQmlApplicationEngine;

#define WTHTTP_QML_INIT( engine ) \
    WTHttp::setQmlEngine( ( engine ) ); \
    qmlRegisterType< WTHttp >( "WTIM", 1, 0, "WTHttp" ); \
    qmlRegisterType< WTHttpRequest >( "WTIM", 1, 0, "WTHttpRequest" )

class WTHttpRequest: public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY( WTHttpRequest )

    // http parameter
    Q_PROPERTY( QString url READ url WRITE setUrl NOTIFY urlChanged )
    Q_PROPERTY( QVariant requestData READ requestData WRITE setRequestData NOTIFY requestDataChanged )
    Q_PROPERTY( QVariant responseData READ responseData WRITE setResponseData NOTIFY responseDataChanged )

    Q_PROPERTY( bool isRunning READ isRunning WRITE setIsRunning NOTIFY isRunningChanged )
    Q_PROPERTY( bool isSucceed READ isSucceed WRITE setIsSucceed NOTIFY isSucceedChanged )
    Q_PROPERTY( bool enabledLog READ enabledLog WRITE setEabledLog NOTIFY enabledLogChanged )
    Q_PROPERTY( int requestTimeElapsed READ requestTimeElapsed WRITE setRequestTimeElapsed NOTIFY requestTimeElapsedChanged )
    Q_PROPERTY( QString errorMessage READ errorMessage WRITE setErrorMessage NOTIFY errorMessageChanged )

    Q_PROPERTY( int timeoutInterval READ timeoutInterval WRITE setTimeoutInterval NOTIFY timeoutIntervalChanged )

public:
    explicit WTHttpRequest();

    virtual ~WTHttpRequest() override;

public:
    static QPointer< WTHttpRequest > create();


    WTHttpRequest &onSucceed(const std::function< void(const QPointer< WTHttpRequest > &request) > &callback);

    WTHttpRequest &onSucceed(const std::function< void() > &callback);


    WTHttpRequest &onError(const std::function< void(const QPointer< WTHttpRequest > &request) > &callback);

    WTHttpRequest &onError(const std::function< void() > &callback);


    WTHttpRequest &onTimeout(const std::function< void(const QPointer< WTHttpRequest > &request) > &callback);

    WTHttpRequest &onTimeout(const std::function< void() > &callback);

public slots:
    void wait();


    QObject *get();

    QObject *get(const QVariant &request);

    QObject *get(const QNetworkRequest &request);

    QObject *get(const QString &url);

    QObject *get(const char *url);

    QObject *upload(const QString &url,const QString &file);


    QObject *post();

    QObject *post(const QVariant &request, const QVariant &data);

    QObject *post(const QNetworkRequest &request, const QVariant &data);

    QObject *post(const QString &url, const QVariant &data);

    QObject *post(const char *url, const QVariant &data);


    QObject *put();

    QObject *put(const QVariant &request, const QVariant &data);

    QObject *put(const QNetworkRequest &request, const QVariant &data);

    QObject *put(const QString &url, const QVariant &data);

    QObject *put(const char *url, const QVariant &data);


    void clear();

    QObject *onSucceed(QJSValue callback);

    QObject *onError(QJSValue callback);

    QObject *onTimeout(QJSValue callback);

protected:
    virtual bool requestHeaderProvider(
        QNetworkRequest &request );

    virtual bool requestUrlSplicer(
        QNetworkRequest &request );

    virtual bool requestDataSerializer(
        QByteArray &data );

    virtual bool responseDataParser(
        const QPointer< QNetworkReply > &reply,
        const QByteArray &               sourceData,
        QVariant &                       targetData,
        QString &                        message,
        int &                             code);

signals:
    void succeed();

    void error();

    void timeout();

    void uploadProgress(const qint64 bytesSent, const qint64 bytesTotal);

    void downloadProgress(const qint64 bytesReceived, const qint64 bytesTotal);

private:
    void handle(const QPointer< QNetworkReply > &reply);

    void onUploadProgress(const qint64 bytesSent, const qint64 bytesTotal);

    void onDownloadProgress(const qint64 bytesReceived, const qint64 bytesTotal);

    void onRequestFinished();

    void onRequestError();

    void onRequestTimeout();

    void doSucceedCallback();

    void doErrorCallback();

    void doTimeoutCallback();

private:
    QNetworkRequest           request_;
    QPointer< QNetworkReply > reply_;
    QTimer                    timeoutTimer_;
    QElapsedTimer             requestTimer_;

    std::function< void() > succeedCallback_;
    std::function< void() > errorCallback_;
    std::function< void() > timeoutCallback_;

    // Property statement code start
private: QString url_;
public: inline QString url() const;
public: inline void setUrl(const QString &newValue);
    Q_SIGNAL void urlChanged(const QString url);

private: QVariant requestData_;
public: inline QVariant requestData() const;
public: inline void setRequestData(const QVariant &newValue);
    Q_SIGNAL void requestDataChanged(const QVariant requestData);

private: QVariant responseData_;
public: inline QVariant responseData() const;
public: inline void setResponseData(const QVariant &newValue);
    Q_SIGNAL void responseDataChanged(const QVariant responseData);

private: int responseCode_;
public: inline int responseCode() const;
public: inline void setResponseCode(const int &newValue);


private: bool isRunning_ = false;
public: inline bool isRunning() const;
public: inline void setIsRunning(const bool &newValue);
    Q_SIGNAL void isRunningChanged(const bool isRunning);

private: bool isSucceed_ = false;
public: inline bool isSucceed() const;
public: inline void setIsSucceed(const bool &newValue);
    Q_SIGNAL void isSucceedChanged(const bool isSucceed);

private: bool enabledLog_ = false;
public: inline bool enabledLog() const;
public: inline void setEabledLog(const bool &newValue);
    Q_SIGNAL void enabledLogChanged(const bool enabledLog);

private: int requestTimeElapsed_ = -1;
public: inline int requestTimeElapsed() const;
public: inline void setRequestTimeElapsed(const int &newValue);
    Q_SIGNAL void requestTimeElapsedChanged(const int requestTimeElapsed);

private: QString errorMessage_;
public: inline QString errorMessage() const;
public: inline void setErrorMessage(const QString &newValue);
    Q_SIGNAL void errorMessageChanged(const QString errorMessage);

private: int timeoutInterval_ = 25 * 1000;
public: inline int timeoutInterval() const;
public: inline void setTimeoutInterval(const int &newValue);
    Q_SIGNAL void timeoutIntervalChanged(const int timeoutInterval);
    // Property statement code end
};

class WTHttp: public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY( WTHttp )

public:
    WTHttp() = default;

    ~WTHttp() override = default;

public:
    static void setQmlEngine(QQmlEngine *qmlEngine);

    static QQmlEngine *qmlEngine();

    static QNetworkAccessManager *networkAccessManager();

    static QNetworkRequest defaultNetworkRequest(const QString &url,bool json=true);

public:
    static QPair< bool, QByteArray > get(
        const QNetworkRequest &request );

    static QPair< bool, QByteArray > get(
        const QString & url );

    static QPair< bool, QByteArray > post(
        const QNetworkRequest &request,
        const QVariant &       data );

    static QPair< bool, QByteArray > post(
        const QString & url,
        const QVariant &data );

public slots:
    WTHttpRequest *createRequest();
};

// .inc include
#include "wthttp.inc"

#endif//WTHTTP_H_
